﻿#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.Linq.Expressions;

namespace AbstractClass
{
    /// <summary>
    /// Represents a calculator for binary operations on generic types.
    /// </summary>
    /// <typeparam name="TLeft">The type of the left operand.</typeparam>
    /// <typeparam name="TRight">The type of the right operand.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public class GenericCalculator<TLeft, TRight, TResult>
    {
        private readonly Func<TLeft, TRight, TResult> _add;
        private readonly Func<TLeft, TRight, TResult> _divide;
        private readonly Func<TLeft, TRight, bool> _equal;
        private readonly Func<TLeft, TRight, bool> _greaterThan, _lessThan;
        private readonly Func<TLeft, TRight, TResult> _multiply;
        private readonly Func<TLeft, TRight, TResult> _subtract;

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericCalculator&lt;TLeft, TRight, TResult&gt;"/> class.
        /// </summary>
        public GenericCalculator()
        {
            ParameterExpression paramA = Expression.Parameter(typeof (TLeft), "a"),
                                paramB = Expression.Parameter(typeof (TRight), "b");
            _add =
                Expression.Lambda<Func<TLeft, TRight, TResult>>(Expression.Add(paramA, paramB), paramA, paramB).Compile();
            _subtract =
                Expression.Lambda<Func<TLeft, TRight, TResult>>(Expression.Subtract(paramA, paramB), paramA, paramB).
                    Compile();
            _multiply =
                Expression.Lambda<Func<TLeft, TRight, TResult>>(Expression.Multiply(paramA, paramB), paramA, paramB).
                    Compile();
            _divide =
                Expression.Lambda<Func<TLeft, TRight, TResult>>(Expression.Divide(paramA, paramB), paramA, paramB).
                    Compile();
            _greaterThan =
                Expression.Lambda<Func<TLeft, TRight, bool>>(Expression.GreaterThan(paramA, paramB), paramA, paramB).
                    Compile();
            _lessThan =
                Expression.Lambda<Func<TLeft, TRight, bool>>(Expression.LessThan(paramA, paramB), paramA, paramB).
                    Compile();
            _equal =
                Expression.Lambda<Func<TLeft, TRight, bool>>(Expression.Equal(paramA, paramB), paramA, paramB).Compile();
        }

        /// <summary>
        /// Adds a value of a specified type with another value of other type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns>The result of the addition.</returns>
        public TResult Add(TLeft a, TRight b)
        {
            return _add(a, b);
        }

        /// <summary>
        /// Subtracts a value of a specified type from another value of other type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns>The result of the subtraction.</returns>
        public TResult Subtract(TLeft a, TRight b)
        {
            return _subtract(a, b);
        }

        /// <summary>
        /// Multiplies a value of a specified type with another value of other type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns>The result of the multiplication.</returns>
        public TResult Multiply(TLeft a, TRight b)
        {
            return _multiply(a, b);
        }

        /// <summary>
        /// Divides a value of a specified type by another value of other type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns>The result of the division.</returns>
        public TResult Divide(TLeft a, TRight b)
        {
            return _divide(a, b);
        }

        /// <summary>
        /// Determines whether a value of a specified type is greater than another value of another type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns><c>true</c> if <paramref name="a"/> is greater than <paramref name="b"/>; otherwise, <c>false</c>.</returns>
        public bool GreaterThan(TLeft a, TRight b)
        {
            return _greaterThan(a, b);
        }

        /// <summary>
        /// Determines whether a value of a specified type is less than another value of another type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns><c>true</c> if <paramref name="a"/> is less than <paramref name="b"/>; otherwise, <c>false</c>.</returns>
        public bool LessThan(TLeft a, TRight b)
        {
            return _lessThan(a, b);
        }

        /// <summary>
        /// Determines whether a value of a specified type is equal to another value of another type.
        /// </summary>
        /// <param name="a">A value of type <typeparamref name="TLeft"/>.</param>
        /// <param name="b">A value of type <typeparamref name="TRight"/>.</param>
        /// <returns><c>true</c> if <paramref name="a"/> is equal to <paramref name="b"/>; otherwise, <c>false</c>.</returns>
        public bool Equals(TLeft a, TRight b)
        {
            return _equal(a, b);
        }
    }
}